#region Head

// 文件名称 ：DebugPro.cs
// 文件描述 ：1.可以打印线程Id和毫秒
//          2.不影响unity控制台跳转(dll)
//          3.可通过Filter屏蔽Log
//          4.可以打印彩色Log
// 创建日期 ：2021/06/30 17:19
// 创 建 者 ：crazybee0124
// 版 本 号 ：1.0

#endregion

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace LogPro
{
    public enum DebugLevel : byte
    {
        /// <summary>
        /// 所有log都打印
        /// </summary>
        Info,
        /// <summary>
        /// 只打印waring和error
        /// </summary>
        Waring,
        /// <summary>
        /// 只打印error
        /// </summary>
        Error,
    }

    // ReSharper disable once UnusedType.Global
    public static class DebugPro
    {
        private static readonly ILogHandler LogHandler = new UnityLogHandler();

        // ReSharper disable once StaticMemberInGenericType
        private static readonly object SyncObject = new object();
        private static readonly StringBuilder MessageBuilder = new StringBuilder(512);
        private static readonly StringBuilder FormatBuilder = new StringBuilder();
        private static readonly StringBuilder StackBuilder = new StringBuilder(1024);

        // ReSharper disable once UnusedAutoPropertyAccessor.Global
        // ReSharper disable once MemberCanBePrivate.Global
        public static bool LogEnable { get; set; } = true;

        public static DebugLevel LogLevel { get; set; } = DebugLevel.Info;

        // ReSharper disable once MemberCanBePrivate.Global
        public static int Filter { get; set; } = (int)LogPro.LogFilter.All;

        // ReSharper disable once MemberCanBePrivate.Global
        /// <summary>
        /// 是否显示毫秒
        /// </summary>
        public static bool MsInfoEnable { get; set; } = true;

        /// <summary>
        /// list, dictionary 等列表 是否换行输出
        /// </summary>
        public static bool WrappingLines
        { get { return FormatObj.WrappingLines; } set { FormatObj.WrappingLines = value; } }

        // ReSharper disable once MemberCanBePrivate.Global
        public static bool ThreadInfoEnable { get; set; } = true;

        // ReSharper disable once UnusedAutoPropertyAccessor.Global
        // ReSharper disable once MemberCanBePrivate.Global
        public static bool StackInfoEnable { get; set; } = false; // ReSharper disable MemberCanBePrivate.Global

        // ReSharper disable UnusedMember.Global

        private static LogFilter _curLogFilter = LogPro.LogFilter.FilterDefault;
        private static LogFilter _preLogFilter;

        /// <summary>
        /// 显示 格式化输出示例
        /// </summary>
        public static void ShowExample()
        {
            Log(FormatObj.TestExample());
        }

        public static void BeginLogFilter(LogFilter filter)
        {
            _preLogFilter = _curLogFilter;
            _curLogFilter = filter;
        }

        public static void EndLogFilter()
        {
            _curLogFilter = _preLogFilter;
        }

        public static LogColor CheckColor(params object[] args)
        {
            if (args == null)
                return LogColor.None;

            for (int i = 0; i < args.Length; i++)
            {
                if (args[i] is LogColor)
                {
                    return (LogColor)args[i];
                }
            }
            return LogColor.None;
        }

        /// <summary>
        /// 格式化输出多个对象，如需带色，在参数的任意位置插入颜色即可，如: DebugPro.Log("my log:",null,LogColor.Yellow,list,Vector3.left))
        /// </summary>
        /// <param name="args">输出对象</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Log(params object[] args)
        {
            LogFilter(_curLogFilter, CheckColor(args), args);
        }

        public static void LogFilter01(params object[] args)
        {
            LogFilter(LogPro.LogFilter.Filter01, CheckColor(args), args);
        }

        public static void LogFilter02(params object[] args)
        {
            LogFilter(LogPro.LogFilter.Filter02, CheckColor(args), args);
        }

        public static void LogFilter03(params object[] args)
        {
            LogFilter(LogPro.LogFilter.Filter03, CheckColor(args), args);
        }

        public static void LogFilter04(params object[] args)
        {
            LogFilter(LogPro.LogFilter.Filter04, CheckColor(args), args);
        }

        public static void LogFilter05(params object[] args)
        {
            LogFilter(LogPro.LogFilter.Filter05, CheckColor(args), args);
        }

        public static void LogFilter06(params object[] args)
        {
            LogFilter(LogPro.LogFilter.Filter06, CheckColor(args), args);
        }

        public static void LogFilter07(params object[] args)
        {
            LogFilter(LogPro.LogFilter.Filter07, CheckColor(args), args);
        }

        public static void LogFilter08(params object[] args)
        {
            LogFilter(LogPro.LogFilter.Filter08, CheckColor(args), args);
        }

        public static void LogFilter09(params object[] args)
        {
            LogFilter(LogPro.LogFilter.Filter09, CheckColor(args), args);
        }

        public static void LogFilter10(params object[] args)
        {
            LogFilter(LogPro.LogFilter.Filter10, CheckColor(args), args);
        }

        public static void LogFilter11(params object[] args)
        {
            LogFilter(LogPro.LogFilter.Filter11, CheckColor(args), args);
        }

        public static void LogFilter12(params object[] args)
        {
            LogFilter(LogPro.LogFilter.Filter12, CheckColor(args), args);
        }

        public static void LogFilter13(params object[] args)
        {
            LogFilter(LogPro.LogFilter.Filter13, CheckColor(args), args);
        }

        public static void LogFilter14(params object[] args)
        {
            LogFilter(LogPro.LogFilter.Filter14, CheckColor(args), args);
        }

        public static void LogFilter15(params object[] args)
        {
            LogFilter(LogPro.LogFilter.Filter15, CheckColor(args), args);
        }

        // public static void LogWatch(string name, string value)
        // {
        //     if (!LogEnable)
        //     {
        //         return;
        //     }
        //     var discard = ((int) LogPro.LogFilter.FilterDefault & Filter) == 0;
        //     if (discard)
        //     {
        //         return;
        //     }
        //     lock (SyncObject)
        //     {
        //         LogHandler.LogWatch(name,value);
        //     }
        // }

        public static void LogWarning(params object[] args)
        {
            if (!LogEnable) return;

            if (DebugLevel.Waring < LogLevel) return;

            lock (SyncObject)
            {
                var color = CheckColor(args);
                LogHandler.LogWarning(WrapMessage(color, args));
            }
        }

        public static void LogError(params object[] args)
        {
            if (!LogEnable) return;

            lock (SyncObject)
            {
                var color = CheckColor(args);
                LogHandler.LogError(WrapMessage(color, args));
            }
        }

        // ReSharper enable UnusedMember.Global
        // ReSharper enable MemberCanBePrivate.Global

        // ReSharper disable once MemberCanBePrivate.Global
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static void LogFilter(LogFilter filter, LogColor color, params object[] args)
        {
            if (!LogEnable) return;
            var discard = ((int)filter & Filter) == 0;
            if (discard) return;
            lock (SyncObject)
            {
                LogHandler.Log(WrapMessage(color, args));
            }
        }

        private static void BeginColor(LogColor color)
        {
            // ReSharper disable StringLiteralTypo
            // ReSharper disable InconsistentlySynchronizedField
            switch (color)
            {
                case LogColor.None:
                    break;

                case LogColor.Red:
                    MessageBuilder.Append("<color=#FF0000FF>");
                    break;

                case LogColor.Green:
                    MessageBuilder.Append("<color=#00FF00FF>");
                    break;

                case LogColor.Blue:
                    MessageBuilder.Append("<color=#0000FFFF>");
                    break;

                case LogColor.White:
                    MessageBuilder.Append("<color=#FFFFFFFF>");
                    break;

                case LogColor.Black:
                    MessageBuilder.Append("<color=#000000FF>");
                    break;

                case LogColor.Yellow:
                    MessageBuilder.Append("<color=#FFEB04FF>");
                    break;

                case LogColor.Cyan:
                    MessageBuilder.Append("<color=#00FFFFFF>");
                    break;

                case LogColor.Magenta:
                    MessageBuilder.Append("<color=#FF00FFFF>");
                    break;

                case LogColor.Gray:
                case LogColor.Grey:
                    MessageBuilder.Append("<color=#808080FF>");
                    break;

                default:
                    throw new ArgumentOutOfRangeException(nameof(color), color, null);
            }
            // ReSharper enable InconsistentlySynchronizedField
            // ReSharper enable StringLiteralTypo
        }

        private static void EndColor(LogColor color)
        {
            // ReSharper disable StringLiteralTypo
            // ReSharper disable InconsistentlySynchronizedField
            switch (color)
            {
                case LogColor.None:
                    break;

                case LogColor.Red:
                case LogColor.Green:
                case LogColor.Blue:
                case LogColor.White:
                case LogColor.Black:
                case LogColor.Yellow:
                case LogColor.Cyan:
                case LogColor.Magenta:
                case LogColor.Gray:
                case LogColor.Grey:
                    MessageBuilder.Append("</color>");
                    break;

                default:
                    throw new ArgumentOutOfRangeException(nameof(color), color, null);
            }
            // ReSharper enable InconsistentlySynchronizedField
            // ReSharper enable StringLiteralTypo
        }

        private static string WrapMessage(LogColor color, params object[] args)
        {
            FormatBuilder.Clear();
            if (args == null)
            {
                FormatBuilder.Append("null");
            }
            else if (args.Length > 0)
            {
                for (int i = 0; i < args.Length; i++)
                {
                    if (i > 0)
                        FormatBuilder.Append(",");

                    var str = FormatObj.ToString(args[i]);
                    FormatBuilder.Append(str);
                }
            }
            MessageBuilder.Clear();

            if (MsInfoEnable) MessageBuilder.AppendFormat("[{0:fff}] ", DateTime.Now);
            if (ThreadInfoEnable)
            {
                MessageBuilder.AppendFormat("Thread:{0:d2} >> ", Thread.CurrentThread.ManagedThreadId);
            }

            MessageBuilder.Append(FormatBuilder);

#if UNITY_EDITOR
            string[] lines = Regex.Split(MessageBuilder.ToString(), @"\n|\t\n");
            MessageBuilder.Clear();
            for (int i = 0; i < lines.Length; i++)
            {
                BeginColor(color);
                MessageBuilder.Append(lines[i]);
                EndColor(color);
                MessageBuilder.AppendLine();
            }
#endif

            if (StackInfoEnable) MessageBuilder.AppendFormat("{0}", GetStackInfo());

            return MessageBuilder.ToString();
        }

        private static string GetStackInfo()
        {
            StackBuilder.Clear();
            var st = new StackTrace(4, true);
            for (var i = 0; i < st.FrameCount; i++)
            {
                var sf = st.GetFrame(i);
                StackBuilder.AppendFormat("\n   {0} [at {1}:{2}]", sf.GetMethod(), sf.GetFileName(),
                    sf.GetFileLineNumber());
            }

            return StackBuilder.ToString();
        }
    }
}